#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <setjmp.h>

#include "include.h"
#include "DataType.h"
#include "SerialIOHelper.h"

#include "SerialIO.h"

static void sigTermHandler();

volatile int sigTermCaught = 0;	// Is SIGTERM caught by handler?
sigjmp_buf environment;

int serialIOMain(int *dataPipe){
	// Register a signal handler for SIGTERM
	struct sigaction newAction;
	newAction.sa_handler = sigTermHandler;
	sigemptyset(&newAction.sa_mask);
	newAction.sa_flags = SA_RESTART;
	if ( sigaction (SIGTERM, &newAction, NULL) == -1) {
		perror("FATAL: serialIOMain(): sigaction() failed.");
		exit(1);
	}

	// Initialize serial port
	int serialPort = initPort(COM_PORT);
	if (serialPort == -1) {
		perror("FATAL: Failed to open serial port");
		exit(1);
	}

	// Close parent end of pipe
	close(dataPipe[0]);

    char readResult[5];
	while (1) {
		sigsetjmp(environment, 1);
		// Handle SIGTERM and SIGUSR1
		if (sigTermCaught) {
			// Exit child process
			#if DEBUG >= 1
			fprintf(stderr, "DEBUG: serialIOMain(): SIGTERM caught, exiting.\n");
			#endif
			break;
		}
        
		// Clear read result
        memset(&readResult, 0, sizeof(readResult));

		// Read 1 byte from serial port
		readPort(serialPort, readResult, 1);
        #if DEBUG >= 3
        fprintf(stderr, "DEBUG: Reading first byte from serial port: %x\n", readResult[0] & 0xff);
        #endif

		// If it is not a header (MSB==1), discard the byte and do nothing
		// If it is a header (MSB==1), read 2 more bytes, setup a Packet struct,
		//	and write it onto the data pipe.
		if (readResult[0] & 0x80) {
			readPort(serialPort, readResult+1, 4);
            #if DEBUG >= 3
            fprintf(stderr, "DEBUG: Reading next 4 bytes from serial port: %x%x%x%x\n", readResult[1] & 0xff, readResult[2] & 0xff, readResult[3] & 0xff, readResult[4] & 0xff);
            #endif
            
            // Setup a packet struct
            Packet packet;
            memset(&packet, 0, sizeof(Packet));

			// Get which mouse the packet is coming from
			switch (readResult[0] & 0x3) {
			case 0:	packet.emulatedMouse = 0; break;
			case 1: packet.emulatedMouse = 1; break;
			case 2: packet.emulatedMouse = 2; break;
			case 3: packet.emulatedMouse = 3; break;
			}
			// Get mouse button state
			// Bit 6 indicates if mouse left button is pressed
			packet.leftPressed = (readResult[0] & 0x40) ? 1 : 0;
			// Get x position. X Position is defined by bit 6~0 and bit 6~3 in byte 1 and 2
			packet.xPos = ((readResult[1] & 0x7F)<<4) + ((readResult[2] & 0x7F)>>3);
			// Get y position. Y Position is defined by bit 6~0 and bit 6~3 in byte 3 and 4
			packet.yPos = ((readResult[3] & 0x7F)<<4) + ((readResult[4] & 0x7F)>>3);

			// Write packet to data pipe
			write(dataPipe[1], &packet, sizeof(Packet));
        }
    }

	return 0;
}

static void sigTermHandler() {
	sigTermCaught = 1;
    siglongjmp(environment, 1);
}
